/*
 * Copyright (c) 2018. paascloud.net All Rights Reserved.
 * 项目名称：paascloud快速搭建企业级分布式微服务平台
 * 类名称：TokenInterceptor.java
 * 创建人：刘兆明
 * 联系方式：paascloud.net@gmail.com
 * 开源地址: https://github.com/paascloud
 * 博客地址: http://blog.paascloud.net
 * 项目官网: http://paascloud.net
 */

package com.weixing.mall.provider.interceptor;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.weixing.mall.base.constant.GlobalConstant;
import com.weixing.mall.base.enums.ResultCodeEnum;
import com.weixing.mall.base.enums.SysCfgCodeEnum;
import com.weixing.mall.base.wrapper.Result;
import com.weixing.mall.base.wrapper.ResultUtil;
import com.weixing.mall.core.annotation.Frequency;
import com.weixing.mall.base.enums.WhetherEnum;
import com.weixing.mall.core.utils.RequestUtil;
import com.weixing.mall.provider.api.model.vo.SysConfigVo;
import com.weixing.mall.provider.api.service.SysConfigServiceFeign;
import com.weixing.mall.provider.model.domain.Member;
import com.weixing.mall.provider.model.domain.MsgCodeLimit;
import com.weixing.mall.provider.model.enums.IsOverRunEnum;
import com.weixing.mall.provider.service.IMemberService;
import com.weixing.mall.provider.service.IMsgCodeLimitService;
import com.weixing.mall.util.ThreadLocalMap;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.task.TaskExecutor;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.Date;
import java.util.concurrent.TimeUnit;

/**
 * 频率限制拦截器
 * <p>
 * The class Token interceptor.
 *
 * @author kfc
 */
@Slf4j
public class FrequencyInterceptor implements HandlerInterceptor {


    private static final int INTERVAL_TIME = 3600000;
    private int SEND_LIMIT = 6;
    private int IP_LIMIT = 6;
    private boolean hasFrequency = false;

    @Autowired
    private IMsgCodeLimitService msgCodeLimitService;
    @Autowired
    private IMemberService memberService;
    @Resource
    private ObjectMapper objectMapper;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Resource
    private TaskExecutor taskExecutor;
    @Autowired
    private SysConfigServiceFeign sysConfigServiceFeign;

    /**
     * Pre handle boolean.
     *
     * @param request  the request
     * @param response the response
     * @param handler  the handler
     * @return the boolean
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        this.hasFrequency = hasFrequency(handler);

        if (this.hasFrequency) {
            String requestData = RequestUtil.getRequestData(request);
            JSONObject jsonObject = JSON.parseObject(requestData);

            String mobile = jsonObject.getString("mobile");

            Result<SysConfigVo> ipLimitResult = sysConfigServiceFeign.getByCode(SysCfgCodeEnum.MSG_SEND_IP_LIMIT.getCode());
            if (ipLimitResult.isSuccess() & null != ipLimitResult.getData()){
                IP_LIMIT = ipLimitResult.getData().getCfgValue();
            }
            Result<SysConfigVo> mobileLimitResult = sysConfigServiceFeign.getByCode(SysCfgCodeEnum.MSG_SEND_HOUR_LIMIT.getCode());
            if (mobileLimitResult.isSuccess() & null != mobileLimitResult.getData()){
                SEND_LIMIT = mobileLimitResult.getData().getCfgValue();
            }

            // ip限制
            String remoteAddr = RequestUtil.getRemoteAddr(request);
            String key = GlobalConstant.REDIS_PRE_IP_MSG_LIMIT + remoteAddr;
            String redisValue = stringRedisTemplate.opsForValue().get(key);
            int ipSendCnt = StringUtils.isBlank(redisValue) ? 0 : Integer.parseInt(redisValue);
            if (ipSendCnt >= IP_LIMIT) {
                sendResponse(response, ResultUtil.error(ResultCodeEnum.CODE_10012006));
                return false;
            }
            MsgCodeLimit msgCodeLimit = msgCodeLimitService.selectByMobile(mobile);
            long currentTimeMillis = System.currentTimeMillis();
            long lastTimeMillis = System.currentTimeMillis();
            if (null != msgCodeLimit
                    && WhetherEnum.NO.getValue().equals(msgCodeLimit.getIsInWhiteList())
                    && currentTimeMillis - lastTimeMillis < INTERVAL_TIME
                    && IsOverRunEnum.OVER.getValue().equals(msgCodeLimit.getIsOverrun())) {
                // 短信发送频率超限
                sendResponse(response, ResultUtil.error(ResultCodeEnum.CODE_10012006));
                return false;
            }
        }
        return true;
    }


    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        if (this.hasFrequency) {
            boolean flag = (boolean) ThreadLocalMap.get(GlobalConstant.TLMK_SMS_FREQUENCY);
            if (flag) {
                String remoteAddr = RequestUtil.getRemoteAddr(request);
                String mobile = RequestUtil.getRequestBodyParam(request, "mobile");
                taskExecutor.execute(() -> this.handleMsgLimit(remoteAddr, mobile));
            }
        }
    }

    private void handleMsgLimit(String remoteAddr, String mobile) {
        // 更新ip发送频率限制
        String key = GlobalConstant.REDIS_PRE_IP_MSG_LIMIT + remoteAddr;
        String redisValue = stringRedisTemplate.opsForValue().get(key);
        int ipSendCnt = StringUtils.isBlank(redisValue) ? 0 : Integer.parseInt(redisValue);
        String newValue = String.valueOf(ipSendCnt + 1);
        if (StringUtils.isBlank(redisValue)) {
            stringRedisTemplate.opsForValue().set(key, newValue, 1, TimeUnit.HOURS);
        } else {
            stringRedisTemplate.opsForValue().set(key, newValue, 0);
        }
        // 更新手机号频率限制
        Date now = new Date();
        //获取请求参数
        MsgCodeLimit msgCodeLimit = msgCodeLimitService.selectByMobile(mobile);
        Member member = memberService.selectByMobile(mobile);
        if (null == msgCodeLimit) {
            createMsgCodeLimit(now, mobile, msgCodeLimit, member);
        } else {
            updateMsgCodeLimit(now, msgCodeLimit, member);
        }
    }

    private void updateMsgCodeLimit(Date now, MsgCodeLimit msgCodeLimit, Member member) {
        if (null != member) {
            if (null == msgCodeLimit.getMemberId()) {
                msgCodeLimit.setMemberId(member.getId());
            }
            if (StringUtils.isBlank(msgCodeLimit.getNickName())) {
                msgCodeLimit.setNickName(member.getNickName());
            }
        }
        int oldSendCnt = msgCodeLimit.getSendCount();
        Date oldLastSendTime = msgCodeLimit.getLastSendTime();
        long toMillis = Math.abs(oldLastSendTime.getTime() - now.getTime());
        if (toMillis < INTERVAL_TIME) {
            int newSendCnt = oldSendCnt + 1;
            msgCodeLimit.setSendCount(newSendCnt);
            msgCodeLimit.setIsOverrun(newSendCnt >= this.SEND_LIMIT ? IsOverRunEnum.OVER.getValue() : IsOverRunEnum.NOT_OVER.getValue());
        } else {
            msgCodeLimit.setSendCount(1);
            msgCodeLimit.setIsOverrun(IsOverRunEnum.NOT_OVER.getValue());
        }
        msgCodeLimit.setLastSendTime(now);
        msgCodeLimitService.updateById(msgCodeLimit);
    }

    private void createMsgCodeLimit(Date now, String mobile, MsgCodeLimit msgCodeLimit, Member member) {
        MsgCodeLimit msgLimit = new MsgCodeLimit();
        msgLimit.setIsInWhiteList(WhetherEnum.NO.getValue());
        msgLimit.setIsOverrun(IsOverRunEnum.NOT_OVER.getValue());
        msgLimit.setLastSendTime(now);
        msgLimit.setMobile(mobile);
        msgLimit.setSendCount(1);
        if (null != member) {
            msgLimit.setMemberId(member.getId());
            msgLimit.setNickName(member.getNickName());
        }
        msgCodeLimitService.save(msgLimit);
    }

    private boolean hasFrequency(Object handler) {
        if (handler instanceof HandlerMethod) {
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            Frequency frequency = handlerMethod.getMethodAnnotation(Frequency.class);
            return frequency != null;
        }
        return false;
    }

    private void sendResponse(HttpServletResponse response, Result result) {
        response.setCharacterEncoding("utf-8");
        response.setContentType("text/html;charset=UTF-8");
        PrintWriter pw = null;
        try {
            OutputStream out = response.getOutputStream();
            pw = new PrintWriter(new OutputStreamWriter(out, "utf-8"));
            // 返回json格式的提示
            pw.println(objectMapper.writeValueAsString(result));
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            pw.flush();
            pw.close();
        }

    }


}
